//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

//The default row layout is a left justified line of display device slots:
//
//+----------------------------------------------------------------------------+
//|*--------------------------+*------------------------+..*------------------+|
//||     display device 0     ||    display device 1    |..| display device n ||
//|| 1920x1080@60 ARGB2101010 || 2560x1600@120 ARGB8888 |..|  WxH@R pixel_fmt ||
//||         x=0 y=0          ||       x=1920 y=0       |..|     x=X y=0      ||
//|+--------------------------++------------------------+..+------------------+|
//+----------------------------------------------------------------------------+
//
//                      <<------------ left justified

#define LOCAL_ERR(fmt,...) \
{\
void *sysname=(void*)udev_device_get_sysname(\
registry[output_name].output.d);\
PERR("output:%s:layout:row:" fmt,sysname,##__VA_ARGS__);\
}

#define LOCAL_LOG(fmt,...) \
{\
void *sysname=(void*)udev_device_get_sysname(\
registry[output_name].output.d);\
LOG("output:%s:layout:row:" fmt,sysname,##__VA_ARGS__);\
}

#if LOG_CFG_LVL>=2
#define LOCAL_LOG_LVL2(fmt,...) LOCAL_LOG(fmt,##__VA_ARGS__);
#else
#define LOCAL_LOG_LVL2 (void)0;
#endif

#define ROW_MAX 16
#if ROW_MAX > 127
#  error "row layout max above 127 (7bits)"
#endif
#define EMPTY_SLOT -1
static s32 row[ROW_MAX];
void output_layout_row_init(void)
{
  s8 slot=0;
  while(1){
    if(slot==ROW_MAX) break;
    row[slot++]=EMPTY_SLOT;
  }
}

//------------------------------------------------------------------------------
#define NONE -1
//          column 0 (x)
//             |
//             v
//line 0 (y)-->+=====+-------------+ ... +-------------+====+
//             |     | prev output | ... | next output |    |
//             |     +-------------+ ... +-------------+    |
//             |                   ^     ^                  |
//             |                   |     |                  |
//             |                   | next column            |
//             |                   |                        |
//             |               last column                  |
//             |              COMPOSITOR SPACE              |
//             +============================================+
#define O_INSERTED 1
static u8 o_insert(s32 output_name,s8 prev_used_slot,s8 next_used_slot)
{
  s32 last_col=-1;
  if(prev_used_slot!=NONE){//here, we have an output on our left and side
    struct output *po=&registry[row[prev_used_slot]].output;
    last_col=po->x+po->current->h-1;
  }

  struct output *o=&registry[output_name].output;
  if(next_used_slot!=NONE){//here, we have an output on our right and side
    struct output *no=&registry[row[next_used_slot]].output;
    s32 next_col=no->x;

    s32 o_w=o->current->h;
    s32 available_w=next_col-last_col-1;

    if(o_w>available_w){
      LOCAL_LOG_LVL2("unable to fit, available_width=%d output_width=%d\n",
                                                                 available_w,o_w);
      return !O_INSERTED;
    }
  }

  o->x=last_col+1;
  o->y=0;
  LOCAL_LOG("insert coords (%d,%d)\n",o->x,o->y);
  return O_INSERTED;
}

s8 output_layout_row_add(s32 output_name)
{
  s8 prev_used_slot=NONE;
  s8 next_used_slot=NONE;
  s8 free_slot=0;

  while(1){
    if(free_slot==ROW_MAX){
      LOCAL_ERR("unable to find an fitting/available row layout slot\n");
      return LWL_ERR;
    }

    //track the previous non empty slot
    if(row[free_slot]!=EMPTY_SLOT){
      prev_used_slot=free_slot;
      ++free_slot;
      continue;
    }

    //lookup for the next slot
    next_used_slot=free_slot+1;
    while(1){
      if(next_used_slot==ROW_MAX){
        next_used_slot=NONE;
        break;
      }
      if(row[next_used_slot]!=EMPTY_SLOT) break;
      ++next_used_slot;
    }

    if(o_insert(output_name,prev_used_slot,next_used_slot)==O_INSERTED){
      row[free_slot]=output_name;
      break;
    }
    free_slot=next_used_slot+1;
  }
  return LWL_OK;
}
#undef O_INSERTED
#undef NONE
//------------------------------------------------------------------------------

void output_layout_row_del(s32 output_name)
{
  s8 slot=0;
  while(1){
    if(slot==ROW_MAX) break;

    if(row[slot]==output_name){
      row[slot]=EMPTY_SLOT;
      break;
    }

    ++slot;
  }
}
